package com.mrogrape.gui;

import java.awt.Color;
import java.awt.Cursor;
import java.awt.EventQueue;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.Icon;
import javax.swing.JLabel;

/**
 * 添加了基本视觉效果的JLabel，使用户能够较为明显的看出其含有动作的特点。
 * 
 * <p>
 * 这个类扩展自JLabel。并且已经添加了效果控制：如光标，颜色的变化。通过这些效果来使它
 * 和一般的Label有所区别。以用于当不需要使用按钮的装饰但却需要实现按钮的基本功能的情况。
 * 
 * <p>
 * 尽管大部分的Swing控件不是<i>线程安全</i>的，但此类的所有方法都<b>是</b>线程安全的。
 * 此外，除非特殊说明，否则此类所有的方法在接受null值时都会抛出NullPointerException.
 * 
 * @author Ice Spring
 * @version 1.0.20110827
 * @see javax.swing.JLabel
 */
public class ButtonLabel extends JLabel{

	/** serialVersionUID */
	private static final long serialVersionUID = -1478826065217084832L;

	/**
	 * 创建默认的ButtonLabel();
	 */
	public ButtonLabel() {

		super();

		this.addDefaultStyle();
	}

	/**
	 * 创建ButtonLabel并指定要显示的Icon和其对齐方式。
	 * 
	 * @param image 要显示的Icon。
	 * @param horizontalAlignment 对齐方式。
	 */
	public ButtonLabel(Icon image, int horizontalAlignment) {

		super(image, horizontalAlignment);

		this.addDefaultStyle();
	}

	/**
	 * 创建ButtonLabel要显示的Icon。
	 * 
	 * @param image 要显示的Icon。
	 */
	public ButtonLabel(Icon image) {

		super(image);

		this.addDefaultStyle();
	}

	/**
	 * 创建ButtonLabel并指定要显示的文本，Icon,和对齐方式。
	 * 
	 * @param text 要显示的文本。
	 * @param icon 要显示的Icon。
	 * @param horizontalAlignment 对齐方式。
	 */
	public ButtonLabel(String text, Icon icon, int horizontalAlignment) {

		super(text, icon, horizontalAlignment);

		this.addDefaultStyle();
	}

	/**
	 * 创建ButtonLabel并指定要显示的文本和对齐方式。
	 * 
	 * @param text 要显示的文本。
	 * @param horizontalAlignment 对齐方式。
	 */
	public ButtonLabel(String text, int horizontalAlignment) {

		super(text, horizontalAlignment);

		this.addDefaultStyle();

	}

	/**
	 * 创建ButtonLabel并指定要显示的文本。
	 * 
	 * @param text 要显示的文本。
	 */
	public ButtonLabel(String text) {

		super(text);

		this.addDefaultStyle();
	}

	/**
	 * 获取默认文本颜色。
	 * 
	 * <p>
	 * 注意，这个颜色并不直接等于{@link javax.swing.JLabel#getForeground()}。
	 * 
	 * @return 默认文本颜色。
	 * @see #setDefaultColor(Color)
	 */
	public Color getDefaultColor() {

		return this.defaultColor;
	}

	/**
	 * 设置默认文本颜色。
	 * 
	 * @param defaultColor 默认文本颜色。
	 * @see #getDefaultColor();
	 */
	public void setDefaultColor(Color defaultColor) {

		if(defaultColor == null){
			throw new NullPointerException("The property value can not be 'null'.");
		}

		Color old = this.defaultColor;

		this.defaultColor = defaultColor;
		this.setColorToDefault();

		this.firePropertyChange("defaultColor", old, this.defaultColor);
	}

	/**
	 * 获取鼠标进入ButtonLabel时组件的前景色。
	 * 
	 * <p>
	 * 这个值显示的是当数遍进入ButtonLabel但还没有按下时组件显示的颜色。
	 * 
	 * @return 鼠标进入ButtonLabel时组件的前景色。
	 * @see #setMouseEnterColor(Color)
	 */
	public Color getMouseEnterColor() {

		return this.mouseEnterColor;
	}

	/**
	 * 设置鼠标进入ButtonLabel时组件的前景色。
	 * 
	 * <p>
	 * 注意：该设置并不会立即起作用。
	 *  
	 * @param mouseEnterColor 鼠标进入ButtonLabel时组件的前景色。
	 * @see #getMouseEnterColor()
	 */
	public void setMouseEnterColor(Color mouseEnterColor) {

		if(mouseEnterColor == null){
			throw new NullPointerException("The property value can not be 'null'.");
		}

		Color old = mouseEnterColor;

		this.mouseEnterColor = mouseEnterColor;

		this.firePropertyChange("mouseEnterColor", old, this.mouseEnterColor);
	}

	/**
	 * 获取鼠标在组件上按下还没有放起时组件的颜色。
	 * 
	 * @return 组件上按下还没有放起时组件的颜色。
	 * @see #setMousePressColor(Color)
	 */
	public Color getMousePressColor() {

		return this.mousePressColor;
	}

	/**
	 * 设置组件上按下还没有放起时组件的颜色。
	 * 
	 * @param mousePressColor 组件上按下还没有放起时组件的颜色。
	 * @see #getMousePressColor()
	 */
	public void setMousePressColor(Color mousePressColor) {

		if(mousePressColor == null){
			throw new NullPointerException("The property value can not be 'null'.");
		}

		Color old = mousePressColor;

		this.mousePressColor = mousePressColor;

		this.firePropertyChange("mousePressColor", old, this.mousePressColor);
	}

	/**
	 * 获取点击过后的组件的颜色。
	 * 
	 * <p>
	 * 这个值指示了当点击过一个ButtonLabel后组件的颜色。把这个值设置为和默认颜色不同
	 * 用户就能清楚的知道他们点击过什么按钮。尤其在选项很多的情况下。比如帮助界面的标题。
	 * 
	 * <p>
	 * 这个颜色仅在启用了访问标记的情况下才有效。
	 * 
	 * @return 点击过后的组件的颜色。
	 * @see #setClickFlagEnable(boolean)
	 * @see #setClickedColor(Color)
	 */
	public Color getClickedColor() {

		return this.clickedColor;
	}

	/**
	 * 设置点击过后的组件的颜色。
	 * 
	 * @param clickedColor 点击过后的组件的颜色。
	 * @see ButtonLabel#getClickedColor()
	 */
	public void setClickedColor(Color clickedColor) {

		if(clickedColor == null){
			throw new NullPointerException("The property value can not be 'null'.");
		}

		Color old = clickedColor;

		this.clickedColor = clickedColor;
		this.setColorToClicked();

		this.firePropertyChange("clickedColor", old, this.clickedColor);
	}

	/**
	 * 检测这个按钮是否处于已经点击过的状态。
	 * 
	 * <p>
	 * <b>注意：</b>这个值仅仅指示了按钮的状态，不能作为按钮是否被访问过的依据。
	 * 
	 * @return 按钮是否处于已经点击过的状态的boolean值:
	 * 			true  : 已经点击过按钮。
	 * 			false : 尚未点击过按钮。
	 * 
	 * @see #setClicked(boolean)
	 */
	public boolean isClicked() {

		return this.clicked;
	}

	/**
	 * 设置按钮的点击状态。
	 * 
	 * @param clicked 按钮是否处于已经点击过的状态的boolean值:
	 * 					true  : 已经点击过按钮。
	 * 					false : 尚未点击过按钮。
	 * @see #isClicked()
	 */
	public void setClicked(boolean clicked) {

		boolean old = clicked;

		this.clicked = clicked;

		if(clicked){
			this.setColorToClicked();
		}else{
			this.setColorToDefault();
		}

		this.firePropertyChange("clicked", old, this.clicked);

	}

	/**
	 * 检查是否启动了按钮点击标记。
	 * 
	 * <p>
	 * 启用这个标记后，如果点击按钮，那么按钮就会变为{@link #setClickedColor(Color)}
	 * 设置的颜色以提示用户。这个值默认为false。
	 * 
	 * @return 是否启动按钮点击标记。
	 * 			true  : 启用
	 * 			false : 不启用
	 * @see #setClickFlagEnable(boolean)
	 */
	public boolean isClickFlagEnable() {

		return this.clickFlagEnable;
	}

	/**
	 * 
	 * 设置是否启动了按钮点击标记。
	 * 
	 * @param clickFlagEnable 是否启动按钮点击标记。
	 * 							true  : 启用
	 * 							false : 不启用
	 * @see #isClickFlagEnable()
	 */
	public void setClickFlagEnable(boolean clickFlagEnable) {

		boolean old = clickFlagEnable;

		this.clickFlagEnable = clickFlagEnable;

		if(clickFlagEnable){
			this.setColorToClicked();
		}else{
			this.setColorToDefault();
		}

		this.firePropertyChange("clickFlagEnable", old, this.clickFlagEnable);

	}

	/**
	 * 添加默认动作。
	 */
	private void addDefaultStyle(){

		this.setCursor(new Cursor(Cursor.HAND_CURSOR));
		this.setColor(this.defaultColor);

		this.addMouseListener(new StyleControler());
	}

	/**
	 * 以安全的方式设置foregroundColor属性
	 * 
	 * @param color 需要设置的颜色。
	 */
	private void setColor(final Color color){
		EventQueue.invokeLater(new Runnable(){

			@Override
			public void run() {

				ButtonLabel.this.setForeground(color);

			}

		});
	}

	/**
	 * 设置颜色为默认。
	 */
	private void setColorToDefault(){

		if(!this.isClickFlagEnable() || !this.isClicked()){
			this.setColor(this.defaultColor);
		}

	}

	/**
	 * 把颜色设置为已经点击过的状态。
	 */
	private void setColorToClicked(){

		if(this.isClickFlagEnable() && this.isClicked()){
			this.setColor(this.clickedColor);
		}

	}


	class StyleControler extends MouseAdapter{

		@Override
		public void mousePressed(MouseEvent e) {

			ButtonLabel.this.setColor(ButtonLabel.this.mousePressColor);
		}



		@Override
		public void mouseReleased(MouseEvent e) {

			ButtonLabel.this.setColor(ButtonLabel.this.mouseEnterColor);
		}



		@Override
		public void mouseClicked(MouseEvent e) {

			ButtonLabel.this.clicked = true;
		}

		@Override
		public void mouseEntered(MouseEvent e) {

			ButtonLabel.this.setColor(ButtonLabel.this.mouseEnterColor);
		}

		@Override
		public void mouseExited(MouseEvent e) {


			ButtonLabel.this.setColorToClicked();
			ButtonLabel.this.setColorToDefault();


		}

	}

	private Color defaultColor    = Color.BLUE;
	private Color mouseEnterColor = Color.ORANGE;
	private Color mousePressColor = Color.RED;
	private Color clickedColor    = Color.DARK_GRAY;

	private boolean clicked = false;
	private boolean clickFlagEnable = false;


}
